package com.i72.penetrate.web;

import com.i72.basic.exception.BusinessException;
import com.i72.basic.tools.BeanHelper;
import com.i72.basic.context.GlobalContext;
import com.i72.penetrate.tools.HttpRequestHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
public class PenetrateServlet extends HttpServlet {


    private static Object LOCK = new Object();

    private static ApplicationContext applicationContext;

    private static Map<String,Object> beanMap = new HashMap<>();
    private static Map<Object,List<Method>> methodMap = new HashMap<>();
    private static Map<Object,List<Method>> paramDefinedMethodMap = new HashMap<>();

    private void setApplicationContext(ServletContext servletContext){
        if(applicationContext==null){
            synchronized (LOCK){
                if(applicationContext==null){
                    applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
                }
            }
        }
    }

    private void setBeanMap(){

        String [] beanNames = applicationContext.getBeanDefinitionNames();

        for(String bean:beanNames){
            if(bean.contains(GlobalContext.getAPPPackage())){
                 beanMap.put(bean,applicationContext.getBean(bean));
            }
        }
    }

    private void setMethodMap(){
        for(Object bean:beanMap.values()){
            methodMap.put(bean,BeanHelper.getMethod(bean.getClass()));

            //获取接口定义的参数
            paramDefinedMethodMap.put(bean,BeanHelper.getMethod(bean.getClass().getInterfaces()[0]));
        }
    }


    private Object findBean(String controllerName){

        List<String> cList = beanMap.keySet().stream().filter(i->{
            return !StringUtils.isEmpty(i) && i.toLowerCase().contains(controllerName.toLowerCase());
        }).collect(Collectors.toList());

        String proxyName = "$AOP$Proxy".toLowerCase();
        Optional<String> c = cList.stream().filter(i->{
            return !StringUtils.isEmpty(i) && i.toLowerCase().contains(proxyName);
        }).findFirst();

        String beanName = c.orElse("");
        if(!StringUtils.isEmpty(beanName)){
            return beanMap.get(beanName);
        }else{
            return beanMap.get(cList.get(0));
        }

    }

    private Method findMethod(Object bean,String methodName){

        if(bean==null || methodMap.get(bean)==null || StringUtils.isEmpty(methodName)){
            return null;
        }

        List<Method> ms = methodMap.get(bean);

        /*
        ms = ms.stream().filter(i->{
            if(i.toGenericString().contains("final") || i.toGenericString().contains("native")){
                return false;
            }
            return true;
        }).collect(Collectors.toList());*/


        Optional<Method> m = ms.stream().filter(i->{
            return !StringUtils.isEmpty(i) &&  i.getName().toLowerCase().contains(methodName.toLowerCase());
        }).findFirst();

        return m.orElse(null);

    }

    private Method findParamsMethod(Object bean,String methodName){

        if(bean==null || paramDefinedMethodMap.get(bean)==null || StringUtils.isEmpty(methodName)){
            return null;
        }

        List<Method> ms = paramDefinedMethodMap.get(bean);

        Optional<Method> m = ms.stream().filter(i->{
            return !StringUtils.isEmpty(i) && i.getName().toLowerCase().contains(methodName.toLowerCase());
        }).findFirst();

        return m.orElse(null);

    }



    @Override
    public void init(ServletConfig config) throws ServletException {


        setApplicationContext(config.getServletContext());

        setBeanMap();

        setMethodMap();

    }



    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {




        HttpRequestHelper requestHelper = applicationContext.getBean(HttpRequestHelper.class);
        //requestHelper.setRequest(req);
        //requestHelper.setResponse(resp);

        if(!requestHelper.validateRequest()){
            requestHelper.http404();
            return;
        }

        Object o = findBean(requestHelper.getController());

        Object result = null;
        boolean success = false;
        String message = "";
        if(o!=null){
            Method m = findMethod(o,requestHelper.getAction());

            if(m!=null) {
                Object [] param = requestHelper.getParam(findParamsMethod(o,requestHelper.getAction()));
                try {

                    //RequestContextHolder.setRequestAttributes(requestHelper);   这个已在RequestContextListener
                    //中实现
                    if(m.getParameterCount()==0) {
                        result = m.invoke(o);
                    }else{
                        result = m.invoke(o,param);
                    }
                    success = true;
                }catch (BusinessException e){
                    success = false;
                    message = e.getBusinessMessage();
                }catch (Exception e){
                    success = false;
                }
            }else{
                log.error(requestHelper.getRequest().getRequestURI()+"没有找到合适的方法执行");
            }
        }else{
            log.error(requestHelper.getRequest().getRequestURI()+"没有找到合适的Controller执行");
        }

        if(success){
            requestHelper.write(result,message);
        }else{
            requestHelper.error();
        }





    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}
